<template>
  <div class="sc-selector" @click.stop>
    <!--    选择框-->
    <div
      ref="scSelectorRef"
      class="sc-selector-holder"
      :class="{
        'disabled':disabled,
        'sc-selector-holder__focus': isFocusSelector,
        'sc-selector-holder__hover': isHoverSelector,
      }"
      @click="clickSelector"
      @mouseenter="mouseEnterSelector"
      @mouseleave="mouseLeaveSelector"
    >
      <div class="sc-selector-holder-content">
        <span v-show="selectedTreeItems.length == 0">{{ placeholder }}</span>
        <div v-show="multiSelect && selectedTreeItems.length > 0">
          <el-tag
            v-for="item in selectedTreeItems"
            closable
            @close="handleDeleteSelect(item)"
            disable-transitions
          >
            {{ item[treeLabelKey] }}
          </el-tag>
        </div>
        <div v-show="!multiSelect && selectedTreeItems.length > 0">
          <div v-for="item in selectedTreeItems" class="sc-selector-input-text">
            {{ item[treeLabelKey] }}
          </div>
        </div>
      </div>
      <div
        class="selector-expand-indicator"
        :class="{
          'selector-expand-indicator__reverse': selectorPopoverVisible,
        }"
      >
        <el-icon color="#A8ABB2">
          <ArrowDown />
        </el-icon>
      </div>
    </div>

    <!--  弹出框-->
    <div
      class="sc-selector-popover"
      ref="scSelectorPopover"
      v-if="selectorPopoverVisible"
    >
      <div class="dropdown-list">
        <div class="top-filter">
          <div class="selector-item filter filter-input">
            <el-input
              style="width: 100%"
              v-model="inputValue"
              :validate-event="false"
              v-bind="inputPropsMixed"
            />
          </div>

          <div
            class="selector-item filter filter-tag"
            v-if="showFilterTags && filterTags && filterTags.length > 0"
          >
            <el-check-tag
              :checked="selectedTag.has(item.val)"
              @change="(check) => handleTagCheckChange(check, item.val)"
              :key="item.val"
              v-for="item in filterTags"
            >
              {{ item.name }}
            </el-check-tag>
          </div>
        </div>

        <div class="selector-item sc-selector-list">
          <el-tree
            v-if="sectionList.length"
            ref="treeRef"
            :data="sectionList"
            :expand-on-click-node="false"
            empty-text=""
            v-bind="treePropsMixed"
          >
            <template #default="{ node, data }">
              <div
                class="tree-item-wrapper"
                @click="handleTreeItemClick({ node, data })"
                @mouseenter="handleTreeItemMouseEnter({ node, data })"
                @mouseleave="handleTreeItemMouseLeave({ node, data })"
              >
                <slot name="tree-item" :data="data" :node="node">
                  <selector-item
                    :data="data"
                    :is-selected="
                      selectedTreeItems.find(
                        (item) =>
                          deepGet(item, treeNodeKey) ==
                          deepGet(data, treeNodeKey)
                      )
                    "
                    :words="data[treeLabelKey]"
                    :highlight-words="highlightWords"
                  />
                </slot>
              </div>
            </template>
          </el-tree>

          <div ref="pageEndIndicator" class="sc-selector-page-end-indicator">
            <span v-if="loadingData">正在加载...</span>
            <span v-else-if="!loadingData && sectionList.length == 0"
              >暂无数据</span
            >
            <span v-else-if="finished">已全部加载</span>
            <span v-else style="cursor: pointer" @click="clickPageEndIndicator"
              >点击加载下一页</span
            >
          </div>
        </div>
      </div>

      <!--  扩展框-->
      <div ref="expandPop" class="selector-expand-pop">
        <slot
          name="expand-pop"
          :node="expandPopSlotProps?.node"
          :data="expandPopSlotProps?.data"
        >
          暂无可显示内容
        </slot>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import {
  IInputProps,
  ITreeItem,
  ITreeProps,
  ITagProps,
} from "@/components/scSelector/types";

import { ArrowDown } from "@element-plus/icons-vue";
import { request } from "@/utils/request";
import ApiResult from "@/api/base/ApiResult";
import { Ref } from "vue";
// import { allOrg } from "@/api/orgManage";

const props = withDefaults(
  defineProps<{
    modelValue: string | number | null | undefined;
    type: string;
    params: object;
    showFilterTags?: boolean;
    showExpandPop?: boolean;
    disabled?: boolean;
    multiSelect?: boolean; // 单选或多选
    placeholder?: string; // 选择器的placeholder
    multiSelectTag?: boolean; // tag 筛选条件多选单选
    // showFirstLevelAsTag?: boolean, // 是否显示一级节点为tag
    timeToShowExtendPop?: "all" | ["section", "node", "leaf"]; // 显示侧边额外信息的时机，all: 总是显示，section: section上显示，node: 节点上显示，leaf：终点节点上显示
    inputProps?: IInputProps;
    treeProps?: ITreeProps;
  }>(),
  {
    placeholder: "请选择",
    multiSelect: false, // 单选
    multiSelectTag: false, // tag 筛选条件多选单选
    showFilterTags: true,
    showExpandPop: true,
    // showFirstLevelAsTag: false,
    timeToShowExtendPop: 'all',
    inputProps: () => ({}),
    treeProps: () => ({}),
  }
);

const treeRef = ref();
const selectedTreeItems = ref<Array<any>>([]),
  filterTags = ref<ITagProps[]>([]);

const getTreeNodeData = async (ids: any) => {
  const rs:ApiResult<any> = await request("merchant/js/query/component/queryList", {
    businessType: props.type,
    ids,
    ...(props.params || {}),
  });

  if (rs.code == 200) {
    return rs.payload
  } else {
    return [];
  }
};

watch(
  () => props.modelValue,
  async (val: any) => {
    let selectData = [],
      selectKey: any = [];
    let select: any = [...selectedTreeItems.value].map((item) =>
      deepGet(item, treeNodeKey.value)
    );
    if (props.multiSelect) {
      selectKey = val;
    } else selectKey = val ? [val] : [];
    if (selectKey.length == 0) {
      selectedTreeItems.value = [];
      return;
    }
    if(selectKey=='local'){
      return
    }
    let noexist = selectKey.find((item) => select.indexOf(item) < 0);
    let noequal = select.find((item) => selectKey.indexOf(item) < 0);
    if (noexist || noequal) selectData = await getTreeNodeData(selectKey);
    else return;
    selectedTreeItems.value = [];
    for (let i = 0; i < selectData.length; i++) {
      selectedTreeItems.value.push(selectData[i]);
    }
    // nextTick(() => {
    //   emits("change-selected", { selected: selectData });
    // });
  },
  {
    immediate: true,
  }
);

const getTagList = async () => {
  const rs = await request("merchant/js/query/component/types", {
    businessType: props.type,
    ...(props.params || {}),
  });
  if (rs.code == 200) {
    filterTags.value = rs.payload;
  }
};

const deepGet = (obj: any, keys: any, defaultVal = "") => {
  return (
    (!Array.isArray(keys)
      ? keys.replace(/\[/g, ".").replace(/\]/g, "").split(".")
      : keys
    ).reduce((o, k) => (o || {})[k], obj) || defaultVal
  );
};

const inputPropsMixed = computed(() => {
  return {
    placeholder: "输入后搜索",
    ...props.inputProps,
  };
});

const treePropsMixed = computed(() => {
  // TODO DeepMerge
  return {
    nodeKey: "id",
    props: {
      label: "title",
      children: "children",
    },
    ...props.treeProps,
  };
});

const emits = defineEmits<{
  (e: "update:modelValue", value: any): void;
  (e: "click-tree-item", value: any): void;
  (e: "mouse-enter-tree-item", value: any): void;
  (e: "mouse-leave-tree-item", value: any): void;
  (
    e: "change-filter",
    value: { selectedTag: Array<string | number>; inputValue: string }
  ): void;
  (e: "change-tag", selectedTag: Array<string | number>): void;
  (e: "change-input", value: string): void;
  (e: "change-selected", value: { selected: ITreeItem[] }): void;
}>();

const elPopoverRef = ref();
const scSelectorRef = ref(); // 展示框ref
const scSelectorPopover = ref(); // 下拉列表ref

// 选择器样式
const isHoverSelector = ref(false);
const isFocusSelector = ref(false);
const mouseEnterSelector = () => {
  isHoverSelector.value = true;
};
const mouseLeaveSelector = () => {
  isHoverSelector.value = false;
};

const selectorPopoverVisible = ref(false);
const clickSelector = () => {
  if(!props.disabled){
    selectorPopoverVisible.value = !selectorPopoverVisible.value;
  }
};

watch(selectorPopoverVisible, (newValue) => {
  if (newValue) {
    scLog("展开下拉列表");
    nextTick(() => {
      watchPageEnd();
      closeableGlobally();
    });
  }
});

// 弹出内容
// const handlePopoverShow = async () => {
//   scLog('展开下拉列表')
//   watchPageEnd()
//   closeableGlobally()
// }
//
// const handlePopoverHide = () => {
//   scLog('下拉列表隐藏')
// }

const closeableGlobally = () => {
  document.addEventListener(
    "click",
    () => {
      scLog("全局关闭事件触发");
      selectorPopoverVisible.value = false;
    },
    {
      once: true,
    }
  );
};

// 数据处理
const loadingData = ref(false);
const finished = ref(false);
const pager = ref({
  currentPage: 0,
  showCount: 20,
});
const sectionList: Ref<ITreeItem[]> = ref([]);

const _loadData = async (refresh = false) => {
  if (loadingData.value) {
    scLog("正在加载数据（防抖）");
    return;
  }

  if (refresh) {
    finished.value = false;
    pager.value.currentPage = 1;
    sectionList.value = [];
  } else {
    if (finished.value) {
      scLog("已经全部加载完成");
      return;
    }
    pager.value.currentPage++;
  }

  loadingData.value = true;
  const rs: any = await request("merchant/js/query/component/queryPage", {
    businessType: props.type,
    ...(props.params || {}),
    type: props.multiSelectTag
      ? [...selectedTag.value]
      : [...selectedTag.value][0],
    text: inputValue.value,
    currentPage: pager.value.currentPage,
    showCount: pager.value.showCount,
  });
  if (rs.code == 200) {
    if (rs.page.currentPage == 1) {
      if (refresh) {
        scSelectorPopover.value
          .getElementsByClassName("dropdown-list")[0]
          .scrollTo(0, 0);
      }
      sectionList.value = rs.payload || [];
    } else sectionList.value = sectionList.value.concat(rs.payload || []);
    if (rs.page.totalPage === pager.value.currentPage) finished.value = true;
    else finished.value = false;
  }

  loadingData.value = false;
};

// 分页，加载下一页
const pageEndIndicator = ref();
let intersectionObserver: IntersectionObserver;
const watchPageEnd = () => {
  scLog("开始监听分页加载情况");

  intersectionObserver = new IntersectionObserver(function (entries) {
    scLog("intersectionObserver triggered");
    const entry = entries[0];
    if (entry.isIntersecting) {
      scLog("页尾相交");
      if (!loadingData.value) {
        scLog("相交且未正在加载，开始加载下一页数据");
        _loadData();
      } else {
        scLog("已经有正在加载的数据，取消本次加载触发");
      }
    } else {
    }
  });

  intersectionObserver.observe(pageEndIndicator.value);
};

const clickPageEndIndicator = () => {
  _loadData();
};

// 自定义KEY
// 树状列表labelKey
const treeLabelKey: ComputedRef<string> = computed(() => {
  return treePropsMixed.value.props?.label ?? "label";
});

// 树状列表节点唯一Key
const treeNodeKey: ComputedRef<string> = computed(() => {
  return treePropsMixed.value["nodeKey"] ?? "id";
});

// input 过滤器
const inputValue: Ref<string> = ref("");
const highlightWords: Ref<string> = ref("");
watch(inputValue, (newValue) => {
  handleInputChange(newValue);
});
let debounceTimer: ReturnType<typeof setTimeout>;
const handleInputChange = (value: string) => {
  scLog("inputChange", value);
  clearTimeout(debounceTimer);
  // sectionList.value = [];
  emits("change-input", value);
  changeFilter();

  debounceTimer = setTimeout(async () => {
    await _loadData(true);
    highlightWords.value = inputValue.value;
  }, 700);
};

// tag过滤器
const selectedTag: Ref<Set<string | number>> = ref(new Set());
const handleTagCheckChange = (status: boolean, item: any) => {
  clearTimeout(debounceTimer);

  if (props.multiSelectTag) {
    // 多选tag
    if (status) {
      selectedTag.value.add(item);
    } else {
      selectedTag.value.delete(item);
    }
  } else {
    // 单选tag
    if (status) selectedTag.value = new Set([item]);
    else selectedTag.value.clear();
  }

  emits("change-tag", [...selectedTag.value]);
  changeFilter();

  debounceTimer = setTimeout(() => {
    _loadData(true);
  }, 700);
};

// 过滤条件change：tag-change，input-change，page-change
const changeFilter = () => {
  emits("change-filter", {
    selectedTag: [...selectedTag.value],
    inputValue: inputValue.value,
  });
};

const expandPop = ref();
const expandPopSlotProps = ref();
const handleTreeItemMouseEnter = ({ node, data }: any) => {
  // scLog('mouseenter', node, data)
  // console.log("handleTreeItemMouseEnter");
  if (!props.showExpandPop) {
    return
  }
  expandPop.value.style.setProperty("display", "block");
  expandPopSlotProps.value = { node, data };
  emits("mouse-enter-tree-item", { node, data });
};

const handleTreeItemMouseLeave = ({ node, data }: any) => {
  // scLog('mouseleave', node, data)
  expandPop.value.style.setProperty("display", "none");
  emits("mouse-leave-tree-item", { node, data });
};

const handleTreeItemClick = ({ node, data }: any) => {
  expandPopSlotProps.value = { node, data };
  if (props.multiSelect) {
    selectedTreeItems.value.push(data);
  } else {
    let _setItem = [];
    _setItem.push(data);
    selectedTreeItems.value = _setItem;
  }

  if (!props.multiSelect) {
    selectorPopoverVisible.value = false;
  }

  const selectedArr = [...selectedTreeItems.value];
  emits("click-tree-item", { selected: selectedArr, node, data });
  let key = selectedArr.map((item) => deepGet(item, treeNodeKey.value));
  if (props.multiSelect) {
    emits("change-selected", { selected: selectedArr });
    emits("update:modelValue", key);
  } else {
    emits("change-selected", { selected: selectedArr });
    emits("update:modelValue", key[0]);
  }
};

const handleDeleteSelect = (treeItemData: any) => {
  selectedTreeItems.value.splice(
    selectedTreeItems.value.findIndex(
      (item) =>
        deepGet(item, treeNodeKey.value) ==
        deepGet(treeItemData, treeNodeKey.value)
    ),
    1
  );
  emits("change-selected", { selected: [...selectedTreeItems.value] });
  emits(
    "update:modelValue",
    [...selectedTreeItems.value].map((item) => deepGet(item, treeNodeKey.value))
  );
};

const scLog = (...log: any) => {
  console.log("sc log ===> ", ...log);
};

// 监听机构
const store = useStore();
const insId = computed(() => {
  return store.state.insId.insId;
});
watch(
  () => insId,
  () => {
    selectedTag.value.clear();
    selectedTreeItems.value = [];
    getTagList();
    _loadData(true);
  },
  {
    deep: true,
  }
);

onMounted(() => {
  getTagList();
});
</script>
<style scoped lang="scss">
.sc-selector {
  position: relative;
}

.sc-selector-holder {
  width: 214px;
  padding: 0 11px;
  position: relative;
  display: inline-flex;
  box-sizing: border-box;
  border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
  box-shadow: 0 0 0 1px var(--el-border-color) inset;
  vertical-align: middle;
  font-size: 14px;
  line-height: 32px;
  color: var(--el-input-text-color, var(--el-text-color-regular));
  cursor: pointer;
  transition: var(--el-transition-box-shadow);

  .selector-expand-indicator {
    display: inline-flex;
    align-items: center;
    transition: all linear 300ms;
  }
  .selector-expand-indicator__reverse {
    transform: rotate(180deg);
  }
}

.sc-selector-holder__focus {
  box-shadow: 0 0 0 1px var(--el-border-color-focus) inset;
}

.sc-selector-holder__hover {
  box-shadow: 0 0 0 1px var(--el-border-color-hover) inset;
}

.sc-selector-holder-content {
  flex: 1;
  width: 0;

  :deep(.el-tag) {
    margin-right: 10px;
  }
  span {
    color: #a8abb2;
  }
  .sc-selector-input-text {
    width: 100%;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
  }
}

.selector-item:not(:last-of-type) {
  margin-bottom: 12px;
}

.sc-selector-list {
  :deep(.tree-item-wrapper) {
    flex: 1;
    width: 0;
  }
}

.sc-selector-popover {
  position: absolute;
  bottom: 0;
  left: 0;
  transform: translateY(calc(100% + 12px));
  z-index: 1000;

  .dropdown-list {
    height: 300px;
    background: white;
    overflow: auto;
    box-shadow: var(--el-box-shadow-light);
    padding: 0 12px;
    max-width: 250px;
  }

  .selector-expand-pop {
    min-width: 10px;
    min-height: 10px;
    padding: 15px;
    position: absolute;
    top: 0;
    right: 0;
    transform: translateX(calc(100% + 12px));
    background: white;
    border-radius: 8px;
    box-shadow: var(--el-box-shadow-light);
    display: none;
  }
}

.sc-selector-popover:after {
  content: "";
  width: 0;
  height: 0;
  border-top: solid 6px transparent;
  border-right: solid 6px transparent;
  border-bottom: solid 6px white;
  border-left: solid 6px transparent;
  position: absolute;
  top: 0;
  left: 104px;
  transform: translateY(-100%);
}

.top-filter {
  position: sticky;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  background: white;
  padding-top: 6px;
  padding-bottom: 6px;

  .filter-tag {
    overflow-x: auto;
    white-space: nowrap;
    .el-check-tag {
      margin-right: 3px;
    }
  }
}

.section-item {
  cursor: pointer;

  .section-title {
    display: flex;
    align-items: center;
    padding: 6px 0;

    .icon {
      width: 4px;
      height: 14px;
      background: var(--el-color-primary);
    }

    .content {
      margin: 0 4px;
    }

    .split-line {
      flex: 1;
      height: 1px;
      background: var(--el-color-info);
    }
  }
}

.sc-selector-page-end-indicator {
  //background: red;
  height: 45px;
  color: var(--el-color-primary);
  padding-top: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: default;
}
.disabled{
    background: #F4F6F9;
  }
</style>
